# -*- coding: iso-8859-1 -*-
""" crypto.keyedHash.michael

    A reference implementation of the Michael Message Integrety Chek (MIC)
    that is defined in IEEE 802.11 Task Group 'i'

    Michael is a 64-bit MIC, with a design strength of 20 bits.

    Copyright (c) 2002 by Paul A. Lambert
    Read LICENSE.txt for license information.
"""
from struct import pack, unpack
from binascii_plus import *


class Michael:
    """ The Michael keyed hash as defined in IEEE 802.11i D2.0 """

    def __init__(self, key=None):
        self.name = 'Michael'
        self.blocksize = 1  # single octets can be hashed by padding to raw block size
        self.raw_block_size = 4  # operates on 32 bits or 4 byte blocks
        self.digest_size = 8  # MIC size of 64 bits or 8 bytes
        self.keySize = 8  # key size is 8 octets
        self.strength = 20
        if key is not None:
            self.setKey(key)

    def __del__(self):
        self.setKey(8 * chr(0))   # feable attempt to clear keys on exit

    def setKey(self, key):
        """ setKey(key) ... key is binary string """
        assert (len(key) == self.keySize), 'Key must be 8 octets'
        self._key = unpack('<II', key)  # unpack into 2 32bit integers

    def __call__(self, data, more=None):
        return self.hash(data)

    def hash(self, data):
        """ Michael keyed hash """
        fullBlocks, extraOctets = divmod(len(data), 4)
        paddedData = data + chr(0x5a) + chr(0) * (7 - extraOctets)
        l, r = self._key
        for i in range(fullBlocks + 2):
            mSub_i = unpack('<I', paddedData[i * 4:i * 4 + 4])[0]  # ith block as 32 bit integer
            l = l ^ mSub_i
            l, r = b(l, r)
        digest = pack('<II', l, r)
        return digest

    def update(self, data):
        raise Exception('No update method supported for Michael keyed hash')

    def digest(self):
        raise Exception('No digest method supported for Michael keyed hash')

    def final(self, data):
        raise Exception('No final method supported for Michael keyed hash')


def b(l, r):
    """ The 'b' block function for the IEEE 802.11i Michael Integrity Check """
    r ^= (((l << 17) & 0xffffffff) | ((l >> 15) & 0x1ffff))       # r = r ^ (l <<< 17)
    l = (l + r) & 0xffffffff                                  # l = (l+r) mod 2**32
    r ^= ((l & 0xff00ff00) >> 8) | ((l & 0x00ff00ff) << 8)        # r = r ^ XSWAP(l)
    l = (l + r) & 0xffffffff                                  # l = (l+r) mod 2**32
    r ^= (((l << 3) & 0xffffffff) | ((l >> 29) & 0x7))            # r  = r ^ (l <<< 3)
    l = (l + r) & 0xffffffff                                  # l = (l+r) mod 2**32
    r ^= (((l << 30) & 0xffffffff) | ((l >> 2) & 0x3fffffff))     # r  = r ^ (l >>> 2)
    l = (l + r) & 0xffffffff                                  # l = (l+r) mod 2**32
    return (l, r)
